package es.javamyadmin.dao;

import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

import es.javamyadmin.modelo.Tables;
import es.javamyadmin.pojo.Mensaje;
import es.javamyadmin.pojo.Error;

/**
 * Clase con los distintos metodos para trabajar con las TABLAS de MySQL.
 * @author Hector Delgado
 * @author Emilio Javier Castillo
 * @version 1.0
 */
public class TablesDAO {

	Connection conexion = null;
	Statement estamento = null;
	PreparedStatement preparada = null;
	ResultSet resultado = null;
	InputStream path = null;

	int pag = -1;
	int elementos = -1;

	/**
	 * Metodo para leer las TABLAS de MySQL
	 * @param user String con el usuario para hacer la conexion a MySQL.
	 * @param pass String con el password para hacer la conexion a MySQL.
	 * @param host String con el servidor donde se encuentra MySQL.
	 * @param port String con el puerto del servidor de MySQL.
	 * @param database String con la Base de Datos de la cual listar las Tablas 
	 * @return
	 */
	public Collection<Tables> leerTables(String user, String pass, String host, String port, String database) {
		Collection<Tables> lista = new ArrayList<Tables>();
		try {
			Class.forName("com.mysql.jdbc.Driver");
			conexion = DriverManager.getConnection("jdbc:mysql://" + host + ":"+ port + "/" + database, user, pass);

			PreparedStatement pstm = conexion.prepareStatement("SHOW TABLE STATUS");
			
			resultado = pstm.executeQuery();
			
			while (resultado.next()) {
				Tables table = new Tables();
				table.setNombreTable(resultado.getString(1));
				table.setNumFilas(resultado.getString(5));
				table.setCotejamiento(resultado.getString(15));
				table.setTipo(resultado.getString(2));
				lista.add(table);
			}
		} catch (SQLException | ClassNotFoundException ex) {
			ex.printStackTrace();
		} finally {
			cerrarConexion();
		}

		return lista;
	}

	/**
	 * Metodo para leer el numero de filas de una Tabla
	 * @param user String con el usuario para hacer la conexion a MySQL.
	 * @param pass String con el password para hacer la conexion a MySQL.
	 * @param host String con el servidor donde se encuentra MySQL.
	 * @param port String con el puerto del servidor de MySQL.
	 * @param database String con la Base de Datos en la cual esta la Tabla. 
	 * @param table String con la Tabla de la cual leer el numero de filas.
	 * @return
	 */
	public Collection<Tables> leerNumFilasTables(String user, String pass, String host, String port, String database, String table) {
		
		Collection<Tables> lista = new ArrayList<Tables>();
		
		try {
			Class.forName("com.mysql.jdbc.Driver");
			conexion = DriverManager.getConnection("jdbc:mysql://" + host + ":"+ port + "/" + database, user, pass);

			PreparedStatement pstm = conexion.prepareStatement("SELECT COUNT(*) \"count\" FROM " + table);
			resultado = pstm.executeQuery();
			while (resultado.next()) {
				Tables miTable = new Tables();
				miTable.setNumFilas(resultado.getString(1));
				lista.add(miTable);
			}
		} catch (SQLException | ClassNotFoundException ex) {
			ex.printStackTrace();
		} finally {
			cerrarConexion();
		}

		return lista;
	}

	/**
	 * Metodo para sacar los detalles de una Tabla. En MySQL describe table. 
	 * @param user String con el usuario para hacer la conexion a MySQL.
	 * @param pass String con el password para hacer la conexion a MySQL.
	 * @param host String con el servidor donde se encuentra MySQL.
	 * @param port String con el puerto del servidor de MySQL.
	 * @param database String con la Base de Datos en la cual esta la Tabla. 
	 * @param tabla String con la Tabla de la cual sacar los detalles.
	 * @return
	 */
	public Collection<Tables> detalleTables(String user, String pass, String host, String port, String database, String tabla) {
		
		Collection<Tables> lista = new ArrayList<Tables>();
		try {
			Class.forName("com.mysql.jdbc.Driver");
			conexion = DriverManager.getConnection("jdbc:mysql://" + host + ":"+ port + "/" + database, user, pass);

			PreparedStatement pstm = conexion.prepareStatement("describe "+ tabla);
			resultado = pstm.executeQuery();
			while (resultado.next()) {
				Tables table = new Tables();
				table.setNombreColum(resultado.getString(1));
				table.setTipoColum(resultado.getString(2));
				table.setNullColum(resultado.getString(3));
				table.setKeyColum(resultado.getString(4));
				table.setDefaultColum(resultado.getString(5));
				table.setExtraColum(resultado.getString(6));
				lista.add(table);
			}
		} catch (SQLException | ClassNotFoundException ex) {
			ex.printStackTrace();
		} finally {
			cerrarConexion();
		}

		return lista;
	}
	
	/**
	 * Metodo para sacar el contenido de una tabla
	 * @param user String con el usuario para hacer la conexion a MySQL.
	 * @param pass String con el password para hacer la conexion a MySQL.
	 * @param host String con el servidor donde se encuentra MySQL.
	 * @param port String con el puerto del servidor de MySQL.
	 * @param database String con la Base de Datos en la cual esta la Tabla. 
	 * @param tabla
	 * @return
	 */
	public Collection<Tables> selecTables(String user, String pass, String host, String port, String database, String tabla) {
		
		Collection<Tables> listaDeListas = new ArrayList<Tables>();

		try {
			Class.forName("com.mysql.jdbc.Driver");
			conexion = DriverManager.getConnection("jdbc:mysql://" + host + ":"+ port + "/" + database, user, pass);
			String tablaVacia = tabla;
			
			if (pag != -1 && elementos != -1) {
				tabla = tabla + " limit " + (pag * elementos - elementos) + ","+ elementos;
			}
			
			PreparedStatement pstm = conexion.prepareStatement("select * from "+ tabla);
			resultado = pstm.executeQuery();
			
			int numeroColumnas = 0;
			boolean primeraVez = true;
			
			while (resultado.next()) {
				if (primeraVez) {
					numeroColumnas = resultado.getMetaData().getColumnCount();
					Tables tab1 = new Tables();
					Collection<Tables> cabecera = new ArrayList<Tables>();
					
					for (int i = 1; i <= numeroColumnas; i++) {
						Tables tab = new Tables();
						tab.setContenido(resultado.getMetaData().getColumnName(i));
						cabecera.add(tab);
					}
					
					tab1.setSelect(cabecera);
					listaDeListas.add(tab1);
					primeraVez = false;
				}
				
				Collection<Tables> listaDatos = new ArrayList<Tables>();
				Tables listaSelect = new Tables();
				
				for (int i = 1; i <= numeroColumnas; i++) {
					Tables tab = new Tables();
					tab.setContenido(resultado.getString(i));
					listaDatos.add(tab);
				}
				
				listaSelect.setSelect(listaDatos);
				listaDeListas.add(listaSelect);
			}
			
			if (primeraVez) {
				Iterator<Tables> a = detalleTables(user, pass, host, port, database, tablaVacia).iterator();
				Collection<Tables> lista = new ArrayList<Tables>();
				
				while (a.hasNext()) {
					Tables tablas =  a.next();
					tablas.setContenido(tablas.getNombreColum().toUpperCase());
					lista.add(tablas);
				}
				
				Tables list = new Tables();
				list.setSelect(lista);
				listaDeListas.add(list);
			}
		} catch (SQLException | ClassNotFoundException ex) {
			ex.printStackTrace();
		} finally {
			cerrarConexion();
		}

		return listaDeListas;
	}

	/**
	 * Metodo que crea la clausula where y llama a update o delete
	 * @param user String con el usuario para hacer la conexion a MySQL.
	 * @param pass String con el password para hacer la conexion a MySQL.
	 * @param host String con el servidor donde se encuentra MySQL.
	 * @param port String con el puerto del servidor de MySQL.
	 * @param database String con la Base de Datos en la cual esta la Tabla. 
	 * @param tabla
	 * @param valor
	 * @param col
	 * @param fil
	 * @param delete
	 * @return
	 */
	public Error where(String user, String pass, String host, String port, String database, String tabla, String valor, int col, int fil, String delete) {
		
		int contador = 0;
		Error error = null;
		String campoModificar = "";
		
		try {
			Collection<Tables> lista2 = detalleTables(user, pass, host, port, database, tabla);
			Iterator<Tables> a = lista2.iterator();
			String condicion[] = new String[lista2.size()];
			
			Collection<Tables> lista = new ArrayList<Tables>();
			
			while (a.hasNext()) {
				Tables tablas = a.next();
				condicion[contador] = tablas.getNombreColum();
				contador++;
			}
			
			Tables list = new Tables();
			list.setSelect(lista);
			
			Class.forName("com.mysql.jdbc.Driver");
			conexion = DriverManager.getConnection("jdbc:mysql://" + host + ":"+ port + "/" + database, user, pass);
			PreparedStatement pstm = conexion.prepareStatement("select * from "+ tabla);
			
			resultado = pstm.executeQuery();
			
			int numeroColumnas;
			contador = 1;
			String where = "where ";
			
			while (resultado.next()) {
				numeroColumnas = resultado.getMetaData().getColumnCount();
				if (contador == fil) {
					contador = 0;
					for (int i = 1; i <= numeroColumnas; i++) {
						if (resultado.getString(i) != null) {
							where = where
									+ condicion[contador].replace("'", "\'")
									+ "='"
									+ resultado.getString(i)
											.replace("'", "\\'") + "' and ";
						} else {
							where = where
									+ condicion[contador].replace("'", "\'")
									+ " is null and ";
						}
						if (i == (col + 1)) {
							campoModificar = condicion[contador];
						}
						contador++;
					}
					break;
				}
				contador++;
			}
			
			where = where.substring(0, where.length() - 4);
			System.out.println("<TABLESDAO> -updateCampo> " + where);
			if (delete.equalsIgnoreCase("false")) {
				error = update(user, pass, host, port, database, tabla, valor, where, campoModificar);
			} else {
				error = delete(user, pass, host, port, database, tabla, where);
				System.out.println("<TABLESDAO> -updateCampo- else.where: "+ where);
			}
		} catch (SQLException ex) {

			error = new Error(Mensaje.getMensaje(path, String.valueOf(ex.getErrorCode())), ex.getMessage().replace("'", ""), "");
			System.out.println("<TablasDAO> -updateCampo- error: "+ error.getError());
		
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} finally {
			cerrarConexion();
		}

		return error;
	}

	/**
	 * Metodo que modifica un campo
	 * @param user String con el usuario para hacer la conexion a MySQL.
	 * @param pass String con el password para hacer la conexion a MySQL.
	 * @param host String con el servidor donde se encuentra MySQL.
	 * @param port String con el puerto del servidor de MySQL.
	 * @param database String con la Base de Datos en la cual esta la Tabla. 
	 * @param tabla
	 * @param valor
	 * @param where
	 * @param campoModificar
	 * @return
	 */
	public Error update(String user, String pass, String host, String port, String database, String tabla, String valor, String where, String campoModificar) {
		
		Error err = null;
		try {
			Class.forName("com.mysql.jdbc.Driver");
			conexion = DriverManager.getConnection("jdbc:mysql://" + host + ":"+ port + "/" + database, user, pass);
			PreparedStatement pstm = conexion.prepareStatement("update " + tabla + " set " + campoModificar + "='" + valor + "' " + where + " limit 1");

			int i = pstm.executeUpdate();
			
			if (i < 1) {
				err = new Error(Mensaje.getMensaje(path, "5003"), "La consulta ejecutada fue: update " + tabla + " set " + campoModificar + "='" + valor + "' " + where,"");
			} else {
				if (i == 1) {
					
					err = new Error("", "", Mensaje.getMensaje(path, "5004"));
					
				} else {
					err = new Error("", "", Mensaje.getMensaje(path, "5004") + ". Se han modificado " + i+ " filas");
				}
			}
		} catch (SQLException ex) {
			err = new Error(Mensaje.getMensaje(path, String.valueOf(ex.getErrorCode())), ex.getMessage().replace("'", ""), "");
			if (err.getError() == null) {
				err.setError(Mensaje.getMensaje(path, "5005"));
			}
			
			System.out.println("<TablasDAO> -update- error: " + err.getError());
			ex.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} finally {
			cerrarConexion();
		}
		return err;
	}
	
	/**
	 * Metodo que crea una tabla
	 * @param user String con el usuario para hacer la conexion a MySQL.
	 * @param pass String con el password para hacer la conexion a MySQL.
	 * @param host String con el servidor donde se encuentra MySQL.
	 * @param port String con el puerto del servidor de MySQL.
	 * @param database String con la Base de Datos en la cual esta la Tabla. 
	 * @param create
	 * @return Error
	 * @throws ClassNotFoundException
	 */
	public Error create(String user, String pass, String host, String port, String database, String create) throws ClassNotFoundException {
		
		Error error = null;
		try {
			Class.forName("com.mysql.jdbc.Driver");			
			conexion = DriverManager.getConnection("jdbc:mysql://" + host + ":"+ port + "/" + database, user, pass);
			PreparedStatement pstm = conexion.prepareStatement(create);
			pstm.executeUpdate();
		} catch (SQLException ex) {
			if (ex.getErrorCode() != 0) {
				error = new Error(Mensaje.getMensaje(path,String.valueOf(ex.getErrorCode())), ex.getMessage().replace("'", ""), "");
			} else {
				error = new Error(Mensaje.getMensaje(path,"1406"), ex.getMessage().replace("'", ""), "");
			}
			System.out.println("<TablasDAO> -create- error: " + error);
			System.out.println("<TablasDAO> -create- errorNum: "+ ex.getErrorCode());
			ex.printStackTrace();
		} finally {
			cerrarConexion();
		}
		return error;
	}

	/**
	 * Metodo que elimina un registro de la tabla
	 * @param user String con el usuario para hacer la conexion a MySQL.
	 * @param pass String con el password para hacer la conexion a MySQL.
	 * @param host String con el servidor donde se encuentra MySQL.
	 * @param port String con el puerto del servidor de MySQL.
	 * @param database String con la Base de Datos en la cual esta la Tabla. 
	 * @param tabla
	 * @param where
	 * @return Error
	 * @throws ClassNotFoundException
	 */
	public Error delete(String user, String pass, String host, String port, String database, String tabla, String where) throws ClassNotFoundException {
		
		Error error = null;
		try {
			Class.forName("com.mysql.jdbc.Driver");
			conexion = DriverManager.getConnection("jdbc:mysql://" + host + ":"+ port + "/" + database, user, pass);
			PreparedStatement pstm = conexion.prepareStatement("delete from "+ tabla + " " + where + " limit 1");
			
			int i = pstm.executeUpdate();

			if (i < 1) {
				error = new Error(Mensaje.getMensaje(path, "5003"), "","");
			}
		} catch (SQLException ex) {
			error = new Error(Mensaje.getMensaje(path, String.valueOf(ex.getErrorCode())), ex.getMessage().replace("'", ""), "");
			ex.printStackTrace();
		} finally {
			cerrarConexion();
		}
		return error;
	}

	/**
	 * Metodo que cambia la estructura de una tabla
	 * @param user String con el usuario para hacer la conexion a MySQL.
	 * @param pass String con el password para hacer la conexion a MySQL.
	 * @param host String con el servidor donde se encuentra MySQL.
	 * @param port String con el puerto del servidor de MySQL.
	 * @param database String con la Base de Datos en la cual esta la Tabla. 
	 * @param tabla
	 * @param query
	 * @return
	 * @throws ClassNotFoundException
	 */
	public Error alter(String user, String pass, String host, String port, String database, String tabla, String query) throws ClassNotFoundException {
		
		Error error = null;
		try {
			Class.forName("com.mysql.jdbc.Driver");
			conexion = DriverManager.getConnection("jdbc:mysql://" + host + ":"+ port + "/" + database, user, pass);
			PreparedStatement pstm = conexion.prepareStatement(query);
			pstm.executeUpdate();

		} catch (SQLException ex) {
			error = new Error(Mensaje.getMensaje(path, String.valueOf(ex.getErrorCode())), ex.getMessage().replace("'", ""), "");
			ex.printStackTrace();
		} finally {
			cerrarConexion();
		}
		return error;
	}

	/**
	 * Metodo que cuenta el numero de filas de una tabla
	 * @param user String con el usuario para hacer la conexion a MySQL.
	 * @param pass String con el password para hacer la conexion a MySQL.
	 * @param host String con el servidor donde se encuentra MySQL.
	 * @param port String con el puerto del servidor de MySQL.
	 * @param database String con la Base de Datos en la cual esta la Tabla. 
	 * @param tabla
	 * @return Error
	 */
	public Error count(String user, String pass, String host, String port, String database, String tabla) {
		
		Error error = null;
		
		try {
			Class.forName("com.mysql.jdbc.Driver");
			conexion = DriverManager.getConnection("jdbc:mysql://" + host + ":"+ port + "/" + database, user, pass);
			PreparedStatement pstm = conexion.prepareStatement("select count(*) from " + tabla);
			resultado = pstm.executeQuery();
			
			while (resultado.next()) {
				error = new Error("", "", resultado.getString(1));
			}

		} catch (SQLException ex) {

			error = new Error(Mensaje.getMensaje(path, String.valueOf(ex.getErrorCode())), ex.getMessage().replace("'", ""), "");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} finally {
			cerrarConexion();
		}

		return error;
	}

	/**
	 * Metodo para cerrar la conexion.
	 */
	public void cerrarConexion() {
		if (resultado != null) {
			try {
				resultado.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			resultado = null;
		}
		if (estamento != null) {
			try {
				estamento.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			estamento = null;
		}
		if (preparada != null) {
			try {
				preparada.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			preparada = null;
		}
		if (conexion != null) {
			try {
				conexion.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			conexion = null;
		}
	}

	public InputStream getPath() {
		return path;
	}

	public void setPath(InputStream inputStream) {
		this.path = inputStream;
	}

	/**
	 * @return the pag
	 */
	public int getPag() {
		return pag;
	}

	/**
	 * @param pag
	 * the pag to set
	 */
	public void setPag(int pag) {
		this.pag = pag;
	}

	/**
	 * @return the elementos
	 */
	public int getElementos() {
		return elementos;
	}

	/**
	 * @param elementos
	 *            the elementos to set
	 */
	public void setElementos(int elementos) {
		this.elementos = elementos;
	}

}